﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using XNAGuiLib.Core.ActionHandling;
using XNAGuiLib.Core.Utilities;

namespace XNAGuiLib.Core.Components
{
    public class Slider : GIComponent
    {
        #region Slider Private Field - _scrollDiff
        private int _scrollDiff;
        #endregion


        #region Slider Protected Field - _rcm
        ResourceContentManager _rcm;
        #endregion

        #region Slider Protected Field - _assetPrefix
        string _assetPrefix;
        #endregion

        #region Slider Protected Field - _length
        int _length;
        #endregion

        #region Slider Protected Field - _orientation
        protected Enums.SliderOrientation _orientation;
        #endregion


        #region Slider Public Field - StepSize
        protected float _stepSizeBlock = 1.0f;
        /// <summary>
        /// gets or sets the step size for moving the block (default to 1.0)
        /// </summary>
        public float StepSize
        {
            get { return this._stepSizeBlock; }
            set { this._stepSizeBlock = value; }
        }
        #endregion

        #region Slider Public Field - StepSizeTrack
        protected float _stepSizeTrack = 5.0f;
        /// <summary>
        /// gets or sets the step size for clicking on the track (default to 5.0)
        /// </summary>
        public float StepSizeTrack
        {
            get { return this._stepSizeTrack; }
            set { this._stepSizeTrack = value; }
        }
        #endregion

        #region Slider Public Field - StepValue
        protected float _stepValue = 1.0f;
        /// <summary>
        /// gets or sets the value one step on the slider represents
        /// </summary>
        public float StepValue
        {
            get { return _stepValue; }
            set { this._stepValue = value; }
        }
        #endregion

        #region Slider Public Field - Value
        /// <summary>
        /// gets or sets the value of the slider (position the block is currently at)
        /// </summary>
        public float Value
        {
            get { return this.GetValue(); }
            set
            {
                if (this._orientation == Enums.SliderOrientation.Horizontal)
                    this.SetValueH(value);
                else if (this._orientation == Enums.SliderOrientation.Vertical)
                    this.SetValueV(value);
            }
        }
        #endregion

        #region Slider Public Field - CurrentStep
        /// <summary>
        /// gets or sets the step of the slider (step the block is at)
        /// </summary>
        public int CurrentStep
        {
            get { return (int)(this.Value / _stepSizeBlock); }
            set { this.Value = (int)(value * _stepSizeBlock); }
        }
        #endregion

        #region Slider Public Field - Steps
        /// <summary>
        /// gets or sets the maximum number of steps for the slider
        /// </summary>
        public int Steps
        {
            get { return (int)(this._length / _stepSizeBlock); }
            set { this._stepSizeBlock = (int)(this._length / value); }
        }
        #endregion


        #region Slider Public Field - Left
        /// <summary>
        /// gets the left GIComponent (beginning of the slider at the 0 value)
        /// </summary>
        public GIComponent Left
        {
            get { return this._gics[0]; }
        }
        #endregion

        #region Slider Public Field - Track
        /// <summary>
        /// gets the track GIComponent
        /// </summary>
        public GIComponent Track
        {
            get { return this._gics[1]; }
        }
        #endregion

        #region Slider Public Field - Right
        /// <summary>
        /// gets the right GIComponent (ending of the slider at the 0 value)
        /// </summary>
        public GIComponent Right
        {
            get { return this._gics[2]; }
        }
        #endregion

        #region Slider Public Field - Block
        /// <summary>
        /// gets the block GIComponent (block which moves on the track)
        /// </summary>
        public GIComponent Block
        {
            get { return this._gics[3]; }
        }
        #endregion


        /* Component mapping for the Background Element
         * ============================================
         * [0]  Left
         * [1]  Track
         * [2]  Right
         * [3]  Block
         */
        public Slider(Game game, ResourceContentManager rcm, int length, Enums.SliderOrientation orientation)
            : base(game)
        {
            this._rcm = rcm;
            this._assetPrefix = "slider_";
            this._length = length;
            this._width = length;
            this._orientation = orientation;

            if (this._orientation == Enums.SliderOrientation.Horizontal)
                InitHorizontal();
            if (this._orientation == Enums.SliderOrientation.Vertical)
                InitVertical();

            MouseAction ma_blockmove = new MouseAction(this._gics[3], this, ActionCommand.SLIDER_CMD_MOVEBLOCK,
                XNAGuiLib.Core.Utilities.Enums.MouseKeys.Left, XNAGuiLib.Core.Utilities.Enums.MouseKeyPositions.Down,
                KeyModifier.NONE);

            MouseAction ma_trackmove = new MouseAction(this._gics[1], this, ActionCommand.SLIDER_CMD_MOVETRACK,
                XNAGuiLib.Core.Utilities.Enums.MouseKeys.Left, XNAGuiLib.Core.Utilities.Enums.MouseKeyPositions.Pressed,
                KeyModifier.NONE);
        }


        #region Slider Protected Method - InitHorizontal()
        /// <summary>
        /// initializes the slider with horizontal orientation
        /// </summary>
        protected void InitHorizontal()
        {
            #region Create Texture2D objects
            Texture2D tx_left = this._rcm.Load<Texture2D>(_assetPrefix + "left_" + (char)_orientation);
            Texture2D tx_track = this._rcm.Load<Texture2D>(_assetPrefix + "track_" + (char)_orientation);
            Texture2D tx_right = this._rcm.Load<Texture2D>(_assetPrefix + "right_" + (char)_orientation);
            Texture2D tx_block = this._rcm.Load<Texture2D>(_assetPrefix + "block");
            #endregion

            #region Create ContentComponent objects
            int dist = (tx_block.Height / 2) - (tx_track.Height / 2);
            int tracklength = this._length - tx_left.Width - tx_right.Width;
            ContentComponent cc_left = new ContentComponent(tx_left);
            ContentComponent cc_track = new ContentComponent(tx_track, new Rectangle(0, 0, tracklength, tx_track.Height));
            ContentComponent cc_right = new ContentComponent(tx_right);
            ContentComponent cc_block = new ContentComponent(tx_block);
            #endregion

            #region Create GIComponent objects
            GIComponent gic_left = Provider.GetInstance(this._game).CreateGIC();
            gic_left.RelativePosition = new Vector2((tx_block.Width / 2), dist);
            gic_left.Add(cc_left);

            GIComponent gic_track = Provider.GetInstance(this._game).CreateGIC();
            gic_track.RelativePosition = new Vector2((tx_block.Width / 2) + tx_left.Width, dist);
            gic_track.Add(cc_track);

            GIComponent gic_right = Provider.GetInstance(this._game).CreateGIC();
            gic_right.RelativePosition = new Vector2((tx_block.Width / 2) + tx_left.Width + tracklength, dist);
            gic_right.Add(cc_right);

            GIComponent gic_block = Provider.GetInstance(this._game).CreateGIC();
            gic_block.RelativePosition = new Vector2(0, 0);
            gic_block.Add(cc_block);
            #endregion

            #region Adding GIComponent objects to the slider
            this.Add(gic_left);
            this.Add(gic_track);
            this.Add(gic_right);
            this.Add(gic_block);
            #endregion
        }
        #endregion

        #region Slider Protected Method - InitVertical()
        /// <summary>
        /// initializes the slider with vertical orientation
        /// </summary>
        protected void InitVertical()
        {
            #region Create Texture2D objects
            Texture2D tx_left = this._rcm.Load<Texture2D>(_assetPrefix + "left_" + (char)_orientation);
            Texture2D tx_track = this._rcm.Load<Texture2D>(_assetPrefix + "track_" + (char)_orientation);
            Texture2D tx_right = this._rcm.Load<Texture2D>(_assetPrefix + "right_" + (char)_orientation);
            Texture2D tx_block = this._rcm.Load<Texture2D>(_assetPrefix + "block");
            #endregion

            #region Create ContentComponent objects
            int dist = (tx_block.Width / 2) - (tx_track.Width / 2);
            int tracklength = this._length - tx_left.Height - tx_right.Height;
            ContentComponent cc_left = new ContentComponent(tx_left);
            ContentComponent cc_track = new ContentComponent(tx_track, new Rectangle(0, 0, tx_track.Width, tracklength));
            ContentComponent cc_right = new ContentComponent(tx_right);
            ContentComponent cc_block = new ContentComponent(tx_block);
            #endregion

            #region Create GIComponent objects
            GIComponent gic_left = Provider.GetInstance(this._game).CreateGIC();
            gic_left.RelativePosition = new Vector2(dist, (tx_block.Height / 2));
            gic_left.Add(cc_left);

            GIComponent gic_track = Provider.GetInstance(this._game).CreateGIC();
            gic_track.RelativePosition = new Vector2(dist, (tx_block.Height / 2) + tx_left.Height);
            gic_track.Add(cc_track);

            GIComponent gic_right = Provider.GetInstance(this._game).CreateGIC();
            gic_right.RelativePosition = new Vector2(dist, (tx_block.Height / 2) + tx_left.Height + tracklength);
            gic_right.Add(cc_right);

            GIComponent gic_block = Provider.GetInstance(this._game).CreateGIC();
            gic_block.RelativePosition = new Vector2(0, 0);
            gic_block.Add(cc_block);
            #endregion

            #region Adding GIComponent objects to the slider
            this.Add(gic_left);
            this.Add(gic_track);
            this.Add(gic_right);
            this.Add(gic_block);
            #endregion
        }
        #endregion

        #region Slider Protected Method - SetValueH(float value)
        /// <summary>
        /// sets the value to the slider (for use on horizontal orientation)
        /// </summary>
        /// <param name="value">value to set</param>
        protected void SetValueH(float value)
        {
            if (value > (this._length))
                this._gics[3].RelativePosition = new Vector2(this._length, 0);
            else if (value < 0)
                this._gics[3].RelativePosition = new Vector2(0, 0);
            else
                this._gics[3].RelativePosition = new Vector2(value, 0);
        }
        #endregion

        #region Slider Protected Method - SetValueV(float value)
        /// <summary>
        /// sets the value to the slider (for use on vertical orientation)
        /// </summary>
        /// <param name="value">value to set</param>
        protected void SetValueV(float value)
        {
            if (value > (this._length))
                this._gics[3].RelativePosition = new Vector2(0, this._length);
            else if (value < 0)
                this._gics[3].RelativePosition = new Vector2(0, 0);
            else
                this._gics[3].RelativePosition = new Vector2(0, value);
        }
        #endregion

        #region Slider Protected Method - GetValue()
        /// <summary>
        /// gets the value of the slider
        /// </summary>
        /// <returns>current value of the slider</returns>
        protected float GetValue()
        {
            float retVal = 0;
            if (this._orientation == Enums.SliderOrientation.Horizontal)
                retVal = this._gics[3].RelativePosition.X * this._stepValue;
            else
                retVal = this._gics[3].RelativePosition.Y * this._stepValue;

            return retVal;
        }
        #endregion


        #region Slider Public Override Method - ExecuteCommand(string command)
        /// <summary>
        /// executes a command on the component (see ActionCommand class for list)
        /// </summary>
        /// <param name="command">command to execute</param>
        public override void ExecuteCommand(string command)
        {
            base.ExecuteCommand(command);

            #region ExecuteCommand | SLIDER_CMD_MOVEBLOCK
            if (command.Equals(ActionCommand.SLIDER_CMD_MOVEBLOCK))
            {
                #region ExecuteCommand | SLIDER_CMD_MOVEBLOCK | Horizontal
                if (this._orientation == Enums.SliderOrientation.Horizontal)
                {
                    int diff = _ms.X - _oms.X;
                    
                    #region Inner | for step size block greater or equals 3
                    if (_stepSizeBlock >= 3)
                    {
                        int halfStep = (int)((_stepSizeBlock / 2.0) - 0.05);

                        if ((this._gics[3].RelativePosition.X + _scrollDiff + diff) < (this._length + 1)
                            && (this._gics[3].RelativePosition.X + _scrollDiff + diff) > 0)
                        {
                            this._scrollDiff += diff;

                            if (_scrollDiff > halfStep)
                            {
                                _scrollDiff = -halfStep;
                                this._gics[3].RelativePosition += new Vector2(_stepSizeBlock, 0);
                            }
                            else if (_scrollDiff < -halfStep)
                            {
                                _scrollDiff = halfStep;
                                this._gics[3].RelativePosition += new Vector2(-_stepSizeBlock, 0);
                            }
                        }
                    }
                    #endregion

                    #region Inner | for step size block smaller 3
                    else
                    {
                        if ((this._gics[3].RelativePosition.X + diff) < (this._length + 1)
                        && (this._gics[3].RelativePosition.X + diff) > 0)
                            this._gics[3].RelativePosition += new Vector2(diff, 0);
                    }
                    #endregion
                }
                #endregion

                #region ExecuteCommand | SLIDER_CMD_MOVEBLOCK | Vertical
                else if (this._orientation == Enums.SliderOrientation.Vertical)
                {
                    int diff = _ms.Y - _oms.Y;

                    #region Inner | for step size block greater or equals 3
                    if (_stepSizeBlock >= 3)
                    {
                        int halfStep = (int)((_stepSizeBlock / 2.0) - 0.05);

                        if ((this._gics[3].RelativePosition.Y + _scrollDiff + diff) < (this._length + 1)
                            && (this._gics[3].RelativePosition.Y + _scrollDiff + diff) > 0)
                        {
                            this._scrollDiff += diff;

                            if (_scrollDiff > halfStep)
                            {
                                _scrollDiff = -halfStep;
                                this._gics[3].RelativePosition += new Vector2(0, _stepSizeBlock);
                            }
                            else if (_scrollDiff < -halfStep)
                            {
                                _scrollDiff = halfStep;
                                this._gics[3].RelativePosition += new Vector2(0, -_stepSizeBlock);
                            }
                        }
                    }
                    #endregion

                    #region Inner | for step size block smaller 3
                    else
                    {
                        if ((this._gics[3].RelativePosition.Y + diff) < (this._length + 1)
                        && (this._gics[3].RelativePosition.Y + diff) > 0)
                            this._gics[3].RelativePosition += new Vector2(0, diff);
                    }
                    #endregion
                }
                #endregion
            }
            #endregion

            #region ExecuteCommand | SLIDER_CMD_MOVETRACK
            else if (command.Equals(ActionCommand.SLIDER_CMD_MOVETRACK))
            {
                #region ExecuteCommand | SLIDER_CMD_MOVETRACK | Horizontal
                if (this._orientation == Enums.SliderOrientation.Horizontal)
                {
                    Console.WriteLine("Value: " + (this.Value + (int)_stepSizeTrack));
                    if (_ms.X > this._gics[3].Rectangle.X)
                        this.Value = (this.Value / _stepValue) + _stepSizeTrack;
                    else if (_ms.X < this._gics[3].Rectangle.X)
                        this.Value = (this.Value / _stepValue) - _stepSizeTrack;
                }
                #endregion

                #region ExecuteCommand | SLIDER_CMD_MOVETRACK | Vertical
                else if (this._orientation == Enums.SliderOrientation.Vertical)
                {
                    if (_ms.Y > this._gics[3].Rectangle.Y)
                        this.Value = this.Value + (int)_stepSizeTrack;
                    else if (_ms.Y < this._gics[3].Rectangle.Y)
                        this.Value = this.Value - (int)_stepSizeTrack;
                }
                #endregion
            }
            #endregion
        }
        #endregion
    }
}